using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using ArkanoidX.Objects;

namespace ArkanoidX.Gums
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// 
    /// Gum is a bonus or ammo for the racket to equip to go on fighting
    /// 
    /// </summary>
    public abstract class Gum : BaseComponent
    {
        public static Type[] GumTypes = new Type[]
        {
            typeof(FreeRacketSwitcher),
            typeof(FighterMaker),
            typeof(Lengthener),
            typeof(BallMultiplier),
            typeof(LifeAwarder)
        };

        public enum State
        {
            Inactive = 0,
            Falling,
            Consumed,
            Missed,
        }

        public static Texture2D[] _sTextures = null;


        public State _state = State.Inactive;
        public Texture2D _texture;
        public Vector2 Position;
        public Vector2 _size;
        public float _droppingSpeed;
        public TimeSpan _lastGameTime = default(TimeSpan);
        public int Score = 4;
        public Bricks _bricks;

        public Level Level
        {
            get
            {
                return _bricks.Level;
            }
        }

        public Rectangle Rectangle
        {
            get
            {
                Vector2 halfSize = _size * 0.5f;
                Rectangle rect = new Rectangle(
                    (int)(Position.X - halfSize.X),
                    (int)(Position.Y - halfSize.Y),
                    (int)Math.Ceiling(_size.X),
                    (int)Math.Ceiling(_size.Y));
                return rect;
            }
        }

        public Gum(Game game, Texture2D texture, Bricks bricks)
            : base(game)
        {
            // TODO: Construct any child components here
            _texture = texture;

            _droppingSpeed = 0.1f;

            _bricks = bricks;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            if (this._state != State.Falling)
            {
                return;
            }

            // falling
            if (_lastGameTime != default(TimeSpan))
            {
                this.Position += new Vector2(0f, this._droppingSpeed)
                    * (gameTime.TotalGameTime - _lastGameTime).Milliseconds;
            }
            _lastGameTime = gameTime.TotalGameTime;

            // collision detection

            // checck collision with the racket
            Racket racket = Level.Racket;
            bool collide = Utility.Collide(racket.Rectangle, this.Rectangle);
            if (collide)
            {
                this._state = State.Consumed;
                this.Act(racket);
                if (((MainGame)Game)._level != null)
                    ((MainGame)Game)._level.Score += this.Score;
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            if (this._state != State.Falling)
            {
                return;
            }

            SpriteBatch sBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            Vector2 halfSize = _size * 0.5f;
            Utility.Draw(sBatch, _texture, this.Position, halfSize, _texture.Bounds, Color.White);
            
            base.Draw(gameTime);
        }

        public static int NumOfGumTypes
        {
            get
            {
                return GumTypes.Length;
            }
        }

        public static void InstallTextures(Game game)
        {
            int numOfGumTypes = NumOfGumTypes;
            _sTextures = new Texture2D[numOfGumTypes];

            try
            {
                for (int i = 0; i < numOfGumTypes; i++)
                {
                    string assetName = GumTypes[i].Name;
                    _sTextures[i] = game.Content.Load<Texture2D>(assetName);
                };
            }
            catch (Exception)
            {
                throw;
            }
        }

        public void Start()
        {
            this._state = State.Falling;
        }

        public abstract void Act(Racket racket);
        
        public static Gum CreateGum(Game game, int iType, Bricks bricks)
        {
            Type type = GumTypes[iType];
            String nameOfType = type.Name;
            System.Reflection.ConstructorInfo ci = type.GetConstructor(new Type[]{
                typeof(Game), typeof(Texture2D), typeof(Bricks)});
            return (Gum)ci.Invoke(new object[]{ game, _sTextures[iType], bricks});
        }

        public static Gum CreateARandomGum(Game game, Random rand, Bricks bricks)
        {
            // TODO: randomly select a gum type here
            // ...

            int rval = rand.Next(NumOfGumTypes);
           

            return CreateGum(game, rval, bricks);
        }

        public void SetContainerGeometric(Vector2 position, Vector2 size)
        {
            this.Position = position;

            // the size of the gum doesn't have to be the same as that of its container
            this._size = size;  
        }
    }
}
